package com.bupt.ilink.jwt;

import java.util.*;


import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.bupt.ilink.service.impl.UserServiceImpl;
import com.bupt.ilink.entity.User;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenProvider {

    @Autowired JwtProperties jwtProperties;

    @Lazy
    @Autowired
    private UserServiceImpl userDetailsService;

    private String secretKey;
    private Algorithm algorithm;
    private JWTVerifier verifier;



    @PostConstruct
    protected void init() {
        secretKey = jwtProperties.getSecretKey();
        algorithm = Algorithm.HMAC256(secretKey);
        verifier = JWT.require(algorithm).build();
    }

    /**
     * 设置token信息，生成token
     * @return java.lang.String
     * @author liuzheng
     * @create 2024-07-27
     **/
    public String createToken(Map<String, String> data, long expiresIn) {
        return JWT.create()
                .withSubject(data.get("sub"))
                .withClaim("type", data.get("type"))
                .withExpiresAt(new Date(System.currentTimeMillis() + expiresIn))
                .sign(algorithm);
    }

    /**
     * 从token中提取并查询得到用户信息
 * @param token
     * @return org.springframework.security.core.Authentication
     * @author liuzheng
     * @create 2024-07-27
     **/
    public Authentication getAuthentication(String token) {
        UserDetails userDetails = this.userDetailsService.loadUserByUserId(getUserIdFromToken(token));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public String getUsername(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    public String getUserIdFromToken(String token) {
        try {
            verifier.verify(token);
            DecodedJWT jwt = JWT.decode(token);
            String userId = jwt.getClaim("sub").asString();
            return userId;
        } catch (JWTDecodeException | IllegalArgumentException e) {
            return "No UserId in Token.";
        }
    }

    public String getTokenTypeFromToken(String token) {
        try {
            verifier.verify(token);
            DecodedJWT jwt = JWT.decode(token);
            String tokenType = jwt.getClaim("type").asString();
            return tokenType;
        } catch (JWTDecodeException | IllegalArgumentException e) {
            return "Invalid token details.";
        }
    }

    public Date getExpiresFromToken(String token) {
        try {
            verifier.verify(token);
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getExpiresAt();
        } catch (JWTDecodeException | IllegalArgumentException e) {
            return null;
        }
    }
    /**
     * 从请求头中解析出token
 * @param req
     * @return java.lang.String
     * @author liuzheng
     * @create 2024-07-27
     **/
    public String resolveToken(HttpServletRequest req) {
        String bearerToken = req.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    /**
     * 检查token是否过期
 * @param token
     * @return boolean
     * @author liuzheng
     * @create 2024-07-27
     **/
    public void validateToken(String token) {
        String id = getUserIdFromToken(token);
        System.out.println("id: " + id);
        String tokenType = getTokenTypeFromToken(token);
        System.out.println("tokenType: " + tokenType);

        if(id == null || tokenType == null){
            throw new InvalidJwtAuthenticationException("Invalid token details.");
        }

        if(userDetailsService.loadUserByUserId(id) == null){
            throw new InvalidJwtAuthenticationException("Invalid token details.");
        }

        Date expiration = getExpiresFromToken(token);

        if (expiration != null && new Date().after(expiration)) {
            throw new InvalidJwtAuthenticationException("Token has expired.");
        }
    }

}
